package cn.biplam.common.tools;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.ArrayList;

import cn.biplam.common.wrapper.ErrorMessage;

/**
 * 动态创建类对象
 * @author XiaoRuxing
 * @version 1.0
 */
public class CallClass implements Serializable{

	private static final long serialVersionUID = -8473728813567163973L;

	/** 类名或者接口名 */
	private String sClassName;

	/** 构造函数参数类型 */
	@SuppressWarnings("unchecked")
	private Class[] cParamTypes;

	/** 构造函数参数值 */
	private Object[] oParams;
	
	/** 匹配步骤，分为精确匹配和模糊匹配 */
	private static  enum MATCH_STEP{
		EXAT_MATCH,
		APPROX_MATCH
	};

	/** 构造函数参数与传入参数类型列表的匹配模式 */
	private char matchModel=Call.MODEL_AUTO_MATCH;
	
	public CallClass(String className){
		this(Call.MODEL_NO_MATCH,className,null,null);
	}

	/**
	 * 构造函数,未传入构造函数参数匹配模式，按照自动匹配模式匹配
	 * @param className   类名
	 * @param paramTypes  构造函数参数列表
	 * @param params      构造函数值列表
	 */
	@SuppressWarnings("unchecked")
	public CallClass(String className,Class[] paramTypes,
			Object[] params) {
		this(Call.MODEL_MANUAL_MATCH,className,paramTypes,params);
	}
	
	/**
	 * 构造函数,传入构造函数参数匹配模式，按照自动匹配模式匹配
	 * @param matchModel  匹配模式
	 * @param className   类名
	 * @param paramTypes  构造函数参数列表
	 * @param params      构造函数值列表
	 */
	@SuppressWarnings("unchecked")
	public CallClass(char matchModel,String className,Class[] paramTypes,
			Object[] params) {
		this.matchModel=matchModel;
		sClassName = className;
		cParamTypes = paramTypes;
		oParams = params;
	}
	
	/**
	 * 构造方法参数个数与传入构造方法参数列表个数匹配；查找类的构造方法列表，当类的构造方法参数个数和传入的参数列表个数相同时记录此构造方法
	 * @param paramTypes 构造方法参数列表
	 * @return 匹配的构造方法参数列表
	 * @throws ErrorMessage
	 */
	@SuppressWarnings("unchecked")
	private ArrayList<Constructor> getNumMatchConstructor(Class[] paramTypes,String sClassName) throws ErrorMessage{
		
		/** 存储与传入参数列表个数匹配的构造方法 */
		ArrayList<Constructor> aContruct=new ArrayList<Constructor>();

		try{
			/** 读类的构造方法列表 */
			Constructor[] construct=Class.forName(sClassName).getConstructors();
			for(int i=0;i<construct.length;i++){
				/** 读构造方法参数列表 */
				Class[] constructParams=construct[i].getParameterTypes();
				/** 判断参数个数是否相等，如果相等则匹配上 */
				if(constructParams.length==paramTypes.length){
					aContruct.add(construct[i]);
				}
			}
			return aContruct;
		}catch(Exception e){
			ErrorMessage eMg=new ErrorMessage(e);
			throw eMg;
		}
	}
	
	/**
	 * 找出与传入的构造方法参数列表匹配的构造方法
	 * @param paramTypes 
	 * @param aParam
	 * @return
	 * @throws ErrorMessage
	 */
	@SuppressWarnings("unchecked")
	private Constructor matchParamTypes(Class[] paramTypes,ArrayList<Constructor> aContructor,MATCH_STEP matchStep) throws ErrorMessage{
		
		/** 构造方法参数列表 */
		Class[] cMatch=null;
		/** 要匹配的参数列表 */
		Class[] cMatched=paramTypes;
		/** 构造方法 */
		Constructor constructor=null;
		
		boolean bMatchFlag=false;
		
		try{
			/** 遍历构造方法列表，如果构造方法参数列表与传入参数列表完全匹配则返回，否则继续遍历 */ 
			for(int i=0;i<aContructor.size();i++){
				cMatch=aContructor.get(i).getParameterTypes();
				if(matchStep==MATCH_STEP.EXAT_MATCH){//精确匹配模式
					bMatchFlag=exactMatchClassType(cMatch,cMatched);
				}else if(matchStep==MATCH_STEP.APPROX_MATCH){//模糊匹配模式
					bMatchFlag=approxMatchClassType(cMatch, cMatched);
				}
				if(bMatchFlag){
					constructor=aContructor.get(i);
					break;
				}
			}
		}catch(Exception e){
			ErrorMessage eMg=new ErrorMessage(e);
			throw eMg;
		}
		return constructor;
	}
	
	/**
	 * 精确匹配参数列表，判断两个参数列表类型是否完全相同
	 * @param match   匹配参数列表
	 * @param matched 被匹配参数列表
	 * @return 是匹配
	 * @throws ErrorMessage 
	 */
	@SuppressWarnings("unchecked")
	private boolean exactMatchClassType(Class[] match,Class[] matched) throws ErrorMessage{
		
		/** 匹配标示 */
		boolean matchFlag=true;
		/** 比较两个列表的每个对应类型是否相等 */
		for(int j=0;j<matched.length;j++){
			if(getPrimitiveClass(match[j])!=matched[j]){
				matchFlag=false;
				break;
			}
		}
		return matchFlag;
	}
	
	/**
	 * 获取基本数据类型的封装类
	 * boolean、byte、char、short、int、long、float 和 double。 
	 * @param object 基本数据类型
	 * @return
	 * @throws ErrorMessage 
	 */
	@SuppressWarnings("unchecked")
	private Class getPrimitiveClass(Class object) throws ErrorMessage{
		if(object.isPrimitive()){
			 if(object == int.class){
				 return Integer.class;
			 }else if(object ==boolean.class){
				 return Boolean.class;
			 }else if(object == byte.class){
				 return Byte.class;
			 }else if(object == char.class){
				 return Character.class;
			 }else if(object == short.class){
				 return Short.class;
			 }else if(object == long.class){
				 return Long.class;
			 }else if(object == float.class){
				 return Float.class;
			 }else if(object == double.class){
				 return Double.class;
			 }else{
				 throw new ErrorMessage("No exist class "+object.getName()+" !");
			 }
		}else{
			return object;
		}
	}
	
	/**
	 * 模糊匹配参数列表，判断匹配列表中每个类型是否与被匹配列表中对应类型相同或者为其超类或超接口
	 * @param match   匹配列表
	 * @param matched 被匹配列表
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private boolean approxMatchClassType(Class[] match,Class[] matched){
		
		/** 匹配标示 */
		boolean matchFlag=true;
		/** 比较匹配列表中每个类型是否与被匹配列表中对应类型相同或者为其超类或超接口 */
		for(int j=0;j<matched.length;j++){
			if(!match[j].isAssignableFrom(matched[j])){
				matchFlag=false;
				break;
			}
		}
		return matchFlag;
	}
	
	/**
	 * 在自动匹配模式下，获取与传入参数类型匹配的构造函数。
	 * @param paramTypes 参数类型列表
	 * @return Constructor
	 * @throws ErrorMessage
	 */
	@SuppressWarnings("unchecked")
	private Constructor getMatchConstructor(Class[] paramTypes,String sClassName) throws ErrorMessage{
		
		/** 存储匹配的构造函数 */
		Constructor constructor=null;
		
		/** 获取个数匹配的构造函数列表 */
		ArrayList<Constructor> aMatchs=getNumMatchConstructor(paramTypes,sClassName);
		/** 如果无构造函数个数与传入参数列表匹配抛出异常 */
		if(aMatchs.size()<1){
			StringBuilder sb = new StringBuilder("none paramTypes match Exception:");
			for(Class ite:paramTypes){
				sb.append(ite.getName());
				sb.append(",\t");
			}
			ErrorMessage eMg=new ErrorMessage(sb.toString());
			throw eMg;
		}
		/** 精确匹配参数类型 */
		constructor=matchParamTypes(paramTypes,aMatchs,MATCH_STEP.EXAT_MATCH);
		/** 如果精确匹配没有匹配上的的参数列表，则模糊进行匹配 */
		if(constructor==null){
			constructor=matchParamTypes(paramTypes,aMatchs,MATCH_STEP.APPROX_MATCH);
		}
		return constructor;
	}
	
	/**
	 * 根据类名及其构造函数参数信息返回类实例对象，对应于有参构造函数
	 * @param call 
	 * @return
	 * @throws ErrorMessage
	 */
	@SuppressWarnings({ "unchecked", "unchecked" })
	public Object getInstance() throws ErrorMessage{
		
		/** 实例对象 */
		Object oInstance=null;

		try {
			/** 取出对象各参数 */
			String className = getClassName();
			Class[] paramTypes = getParamTypes();
			Object[] params =  getParams();

			/** 获取类 */
			Class classType=Class.forName(className);
			
			/** 生成构造方法 */
			Constructor constructor = null;
			/** 如果匹配模式为自动匹配则调用自动匹配过程创建构造函数，否则按照传入参数类型创建构造函数 */
			if(matchModel==Call.MODEL_AUTO_MATCH){
				constructor = getMatchConstructor(paramTypes,className);
			}else{
				constructor = classType.getConstructor(paramTypes);
			}
			/** 用指定构造方法创建类实例对象 */
			oInstance=constructor.newInstance(params);

		} catch (Exception e) {
			Exception e1;
			try {
				e1 = (Exception)e.getCause();
			} catch (ClassCastException e2) {//如果目标类异常，抛出的是java.lan.Error
				Error aa=(Error)e.getCause();
				e1=(Exception) aa.getCause();
			}
			if(e1!=null){
				if (e1 instanceof ErrorMessage) {
					throw (ErrorMessage)e1;
				}else{
					throw new ErrorMessage(e1);
				}
			}else{
				throw new ErrorMessage(e,"GetInstance runtime error!");
			}
		} 
		return oInstance;
	}

	/**
	 * 获取类名
	 * @return
	 */
	public String getClassName() {
		return sClassName;
	}

	/**
	 * 设置类名
	 * @param className
	 */
	public void setClassName(String className) {
		sClassName = className;
	}

	/**
	 * 获取构造函数参数类型
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Class[] getParamTypes() {
		return cParamTypes;
	}

	/**
	 * 设置构造函数参数类型
	 * @param paramTypes
	 */
	@SuppressWarnings("unchecked")
	public void setParamTypes(Class[] paramTypes) {
		cParamTypes = paramTypes;
	}

	/**
	 * 获取构造函数参数值
	 * @return
	 */
	public Object[] getParams() {
		return oParams;
	}

	/**
	 * 设置构造函数参数值
	 * @param params
	 */
	public void setParams(Object[] params) {
		oParams = params;
	}

}
